package org.spiderflow.common.utils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * 对象数据量比较大时Kryo 比较慢，FST比较快
 * Kryo 要求对象及其字段引用对象必须有无参构造函数
 *
 * @author 张中海
 * 代码来自 http://javatechniques.com/blog/faster-deep-copies-of-java-objects/
 */
public class DgbObjects {
    private DgbObjects() {
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object object) {
        return (T) object;
    }

    public static boolean equals(Object a, Object... objects) {
        for (Object obj : objects) {
            boolean isEquals = Objects.equals(a, obj);
            if (!isEquals) {
                return false;
            }
        }
        return true;
    }

    public static int equalsOr(Object a, Object... objects) {
        if (objects == null) {
            return -1;
        }
        for (int i = 0; i < objects.length; i++) {
            if (Objects.equals(a, objects[i])) {
                return i;
            }
        }
        return -1;
    }

    public static boolean isEmpty(Collection<?> collection) {
        return (collection == null || collection.isEmpty());
    }

    public static boolean isEmpty(Map<?, ?> map) {
        return (map == null || map.isEmpty());
    }

    public static boolean isEmpty(String arg) {
        return (arg == null || arg.trim().isEmpty());
    }

    public static boolean isNotEmpty(Collection<?> collection) {
        return (collection != null && !collection.isEmpty());
    }

    public static boolean isNotEmpty(Map<?, ?> map) {
        return (map != null && !map.isEmpty());
    }

    public static boolean isNotEmpty(String arg) {
        return (arg != null && !arg.trim().isEmpty());
    }

    public static boolean isAllEmpty(String... args) {
        for (String arg : args) {
            if (isNotEmpty(arg)) {
                return false;
            }
        }
        return true;
    }

    public static boolean isNoneEmpty(String... args) {
        for (String arg : args) {
            if (isEmpty(arg)) {
                return false;
            }
        }
        return true;
    }

    public static boolean isAnyEmpty(String... args) {
        for (String arg : args) {
            if (isEmpty(arg)) {
                return true;
            }
        }
        return false;
    }

    public static String getStackTrace() {
        StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        return getStackTrace(ste);
    }

    public static void sleep(long timeout) {
        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(timeout));
    }

    public static String getStackTrace(Thread t) {
        StackTraceElement[] ste = t.getStackTrace();
        return getStackTrace(ste);
    }

    public static String getStackTrace(StackTraceElement[] ste) {
        StringBuilder sb = new StringBuilder();

        for (int i = 2; i < ste.length; i++) {
            sb.append(i == 2 ? "" : "\t").append(ste[i].toString()).append(System.lineSeparator());
        }
        return sb.toString();
    }


    private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);

    public static int calcHashMapCapacity(int size) {
        if (size < 3) {
            return size + 1;
        }
        if (size < MAX_POWER_OF_TWO) {
            return (int) (size / 0.75F + 1.0F);
        }
        return Integer.MAX_VALUE;
    }

    public static <K, V> Map<K, V> newHashMap(int expectedSize) {
        return new HashMap<>(calcHashMapCapacity(expectedSize));
    }

    public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
                                                             Function<? super T, ? extends U> valueMapper,
                                                             boolean replaceOld) {
        return Collectors.toMap(keyMapper, valueMapper, (v1, v2) -> {
            if (replaceOld) {
                return v2;
            } else {
                return v1;
            }
        });
    }

}
